1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.hipaudio.audio; 12 13 public import hip.hipaudio.audiosource; 14 public import hip.api.audio; 15 import hip.config.audio; 16 17 //Backends 18 19 20 21 import hip.audio_decoding.audio; 22 import hip.math.utils:getClosestMultiple; 23 import hip.util.reflection; 24 import hip.error.handler; 25 26 27 /** 28 * This is an interface that should be created only once inside the application. 29 * Every audio function is global, meaning that every AudioSource will refer to the player 30 */ 31 public interface IHipAudioPlayer 32 { 33 //LOAD RELATED 34 public bool play_streamed(AHipAudioSource src); 35 public IHipAudioClip getClip(); 36 public IHipAudioClip loadStreamed(string path, uint chunkSize); 37 public void updateStream(AHipAudioSource source); 38 public AHipAudioSource getSource(bool isStreamed); 39 40 public void onDestroy(); 41 public void update(); 42 } 43 44 class HipAudio 45 { 46 public static bool initialize(HipAudioImplementation implementation = HipAudioImplementation.OpenAL, 47 bool hasProAudio = false, 48 bool hasLowLatencyAudio = false, 49 int optimalBufferSize = 4096, 50 int optimalSampleRate = 44_100) 51 { 52 ErrorHandler.startListeningForErrors("HipremeAudio initialization"); 53 _hasInitializedAudio = true; 54 HipAudio.is3D = is3D; 55 audioInterface = getAudioInterface(implementation); 56 HipAudio.hasProAudio = hasProAudio; 57 HipAudio.hasLowLatencyAudio = hasLowLatencyAudio; 58 HipAudio.optimalBufferSize = optimalBufferSize; 59 HipAudio.optimalSampleRate = optimalSampleRate; 60 return ErrorHandler.stopListeningForErrors(); 61 } 62 @ExportD static bool pause(AHipAudioSource src) 63 { 64 src.isPlaying = false; 65 return false; 66 } 67 @ExportD static bool play_streamed(AHipAudioSource src) 68 { 69 audioInterface.play_streamed(src); 70 src.isPlaying = true; 71 return false; 72 } 73 @ExportD static IHipAudioClip getClip(){return audioInterface.getClip();} 74 75 /** 76 * Loads a file from disk, sets the chunkSize for streaming and does one decoding frame 77 */ 78 @ExportD static IHipAudioClip loadStreamed(string path, uint chunkSize = ushort.max+1) 79 { 80 chunkSize = getClosestMultiple(optimalBufferSize, chunkSize); 81 IHipAudioClip buf = audioInterface.loadStreamed(path, chunkSize); 82 return buf; 83 } 84 85 @ExportD static void updateStream(HipAudioSource source) 86 { 87 audioInterface.updateStream(source); 88 } 89 @ExportD static AHipAudioSource getSource(bool isStreamed = false, IHipAudioClip clip = null) 90 { 91 if(isStreamed) ErrorHandler.assertExit(clip !is null, "Can't get streamed source without any buffer"); 92 HipAudioSource ret = cast(HipAudioSource)audioInterface.getSource(isStreamed); 93 if(clip) 94 ret.clip = clip; 95 return ret; 96 } 97 @ExportD static void onDestroy() 98 { 99 if(audioInterface !is null) 100 audioInterface.onDestroy(); 101 audioInterface = null; 102 } 103 104 static void update() 105 { 106 if(audioInterface !is null) 107 audioInterface.update(); 108 } 109 110 private static IHipAudioPlayer getAudioInterface(HipAudioImplementation impl, 111 bool hasProAudio = false, 112 bool hasLowLatencyAudio = false, 113 int optimalBufferSize = 4096, 114 int optimalSampleRate = 44_100) 115 { 116 import hip.console.log; 117 final switch(impl) 118 { 119 case HipAudioImplementation.WebAudio: 120 { 121 static if(HasWebAudio) 122 { 123 import hip.hipaudio.backend.webaudio.player; 124 return new HipWebAudioPlayer(AudioConfig.musicConfig); 125 } 126 else 127 { 128 loglnWarn("Tried to use WebAudio implementation, but not in WebAssembly. No audio available"); 129 goto case HipAudioImplementation.Null; 130 } 131 } 132 case HipAudioImplementation.OpenSLES: 133 static if(HasOpenSLES) 134 { 135 import hip.hipaudio.backend.opensles.player; 136 return new HipOpenSLESAudioPlayer(AudioConfig.androidConfig, 137 hasProAudio, 138 hasLowLatencyAudio, 139 optimalBufferSize, 140 optimalSampleRate); 141 break; 142 } 143 case HipAudioImplementation.XAudio2: 144 static if(HasXAudio2) 145 { 146 import hip.hipaudio.backend.xaudio.player; 147 loglnInfo("Initializing XAudio2 with audio config ", AudioConfig.musicConfig); 148 return new HipXAudioPlayer(AudioConfig.musicConfig); 149 } 150 else 151 { 152 loglnWarn("Tried to use XAudio2 implementation, but no XAudio2 version was provided. OpenAL will be used instead"); 153 goto case HipAudioImplementation.OpenAL; 154 } 155 case HipAudioImplementation.AVAudioEngine: 156 { 157 static if(HasAVAudioEngine) 158 { 159 import hip.hipaudio.backend.avaudio.player; 160 return new HipAVAudioPlayer(AudioConfig.androidConfig); 161 } 162 else 163 { 164 loglnWarn("Tried to use AVAudioEngine implementation, but no AVAudioEngine found. OpenAL will be used instead"); 165 goto case HipAudioImplementation.OpenAL; 166 } 167 } 168 case HipAudioImplementation.OpenAL: 169 { 170 static if(HasOpenAL) 171 { 172 import hip.hipaudio.backend.openal.player; 173 //Please note that OpenAL HRTF(spatial sound) only works with Mono Channel 174 return new HipOpenALAudioPlayer(AudioConfig.musicConfig); 175 } 176 else 177 { 178 loglnWarn("Tried to use OpenAL implementation, but no OpenAL version was provided. No audio available."); 179 goto case HipAudioImplementation.Null; 180 } 181 } 182 case HipAudioImplementation.Null: 183 { 184 import hip.hipaudio.backend.nullaudio; 185 loglnWarn("No AudioInterface was found. Using NullAudio"); 186 return new HipNullAudio(); 187 } 188 } 189 } 190 191 192 193 protected __gshared bool hasProAudio; 194 protected __gshared bool hasLowLatencyAudio; 195 protected __gshared int optimalBufferSize; 196 protected __gshared int optimalSampleRate; 197 private __gshared bool is3D; 198 private __gshared uint activeSources; 199 200 __gshared IHipAudioPlayer audioInterface; 201 202 //Debug vars 203 private __gshared bool _hasInitializedAudio = false; 204 public bool hasInitializedAudio() => _hasInitializedAudio; 205 }